﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Transform;
using Spring.Data.NHibernate.Support;
using Spring.Stereotype;

namespace TSites.Core.Repository
{
	[Repository]
	public class NHibernateRepository<T, IdT> : HibernateDaoSupport, IRepository<T, IdT>
		where T : class
	{
		protected ISession CurrentSession
		{
			get { return SessionFactory.GetCurrentSession(); }
		}

		private IQuery PrepareQuery(string spName, params object[] parameters)
		{
			// create an IQuery based on your named query, specifying parameters
			IQuery namedQuery = CurrentSession.GetNamedQuery(spName);
			int paramsLen;
			if (parameters != null && (paramsLen = parameters.Length) > 0)
			{
				for (int i = 0; i < paramsLen; i++)
				{
					namedQuery.SetParameter(namedQuery.NamedParameters[i], parameters[i]);
				}
			}
			namedQuery.SetResultTransformer(Transformers.AliasToBean(typeof(T)));
			return namedQuery;
		}

		#region IRepository<T,TId> Members

		public T GetByID(IdT id)
		{
			return CurrentSession.Get<T>(id);
		}

		public T GetByID(IdT id, LockMode lckMode)
		{
			return CurrentSession.Get<T>(id, lckMode);
		}

		public T GetByCriteria(ICriteria criteria)
		{
			return (T)criteria.UniqueResult();
		}

		public IList<T> GetList()
		{
			return CurrentSession.CreateCriteria(typeof(T)).List<T>();
		}

		public IList<T> GetList(ICriteria criteria, bool asc, params string[] orderByCol)
		{
            if(orderByCol != null)
            {
                foreach (string item in orderByCol)
                {
                    criteria.AddOrder(asc ? Order.Asc(item) : Order.Desc(item));
                }
            }
			return criteria.List<T>();
		}

		public IList GetList(ICriteria criteria, int pageIndex, int pageSize, out int totalItems)
		{
			// Copy the ICriteria query to get a row count as well
			ICriteria critCount = CriteriaTransformer.Clone(criteria)
				.SetProjection(Projections.RowCount());
			critCount.ClearOrders();

			// NOW add the paging vars to the original query
			criteria.SetMaxResults(pageSize)
				.SetFirstResult((pageIndex - 1) * pageSize);

			// Set up a multi criteria to get your data in a single trip to the database
			IMultiCriteria multCrit = CurrentSession.CreateMultiCriteria()
				.Add(criteria)
				.Add(critCount);

			// Get the results
			IList results = multCrit.List();

			totalItems = Convert.ToInt32(((IList)results[1])[0]);
			return (IList)results[0];
		}

		// LINQ supported
		public IQueryable<T> GetByLinq()
		{
			return null;
		}

		public IdT Insert(T entity)
		{
			IdT inst = (IdT)CurrentSession.Save(entity);
			return inst;
		}

		public void Insert(List<T> list)
		{
			ISession session;
			try
			{
				session = CurrentSession;
				foreach (T entity in list)
				{
					session.Save(entity);
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
			finally
			{ }
		}

		public void Update(T entity)
		{
			CurrentSession.Update(entity);
		}

		public void InsertOrUpdate(T entity)
		{
			CurrentSession.SaveOrUpdate(entity);
		}

		public void Delete(T entity)
		{
			CurrentSession.Delete(entity);
		}

		public T ExecuteSingleQuery(string spName, params object[] parameters)
		{
			return PrepareQuery(spName, parameters).UniqueResult<T>();
		}

		public IList<T> ExecuteQuery(string spName, params object[] parameters)
		{
			return PrepareQuery(spName, parameters).List<T>();
		}

		public int ExecuteNonQuery(string spName, params object[] parameters)
		{
			// create an IQuery based on your named query, specifying parameters
			IQuery namedQuery = CurrentSession.GetNamedQuery(spName);
			int paramsLen;
			if (parameters != null && (paramsLen = parameters.Length) > 0)
			{
				for (int i = 0; i < paramsLen; i++)
				{
					namedQuery.SetParameter(namedQuery.NamedParameters[i], parameters[i]);
				}
			}
			return namedQuery.ExecuteUpdate();
		}

		#endregion
	}
}